#include "handy/handy/handy.h"
#include <algorithm>
#include <list>
#include <map>
#include <stdio.h>
#include <string>
#include <stdint.h>
#include <arpa/inet.h>
#include "common.h"
#include "xudp.pb.h"
using namespace std;
using namespace handy;

unsigned int g_id = 0;

std::map<int, std::shared_ptr<xmsg> > g_unack_msg;
string g_content;
int g_una = 0;
int g_exit = 0;

shared_ptr<xmsg> gen_msg() {
	shared_ptr<xmsg> msg = make_shared<xmsg>();
	msg->set_id(g_id++);
	msg->set_buf(g_content.c_str(), MSG_SIZE);
	return msg;
}


int get_content(char* b, int len) {
	xpackage p;
	p.set_cmd(CMD_DATA);
	for(int i = 0; i < PARTIAL_NUM; i++){
        int id = g_id-i-1;
        if(g_unack_msg.find(id) != g_unack_msg.end()){
            xmsg* m = p.add_msg();
            *m = *(g_unack_msg[id]);
        }
    }
	bool suc = p.SerializeToArray(b, len);
	if (!suc) {
		fatal("serialize failed");
		return -1;
	}
	return p.ByteSize();
}

int main() {
	g_content = random_string(500);

	setloglevel("INFO");
	setlogfile("test_client_partial_udp");
	EventBase base;
	Signal::signal(SIGINT, [&] { base.exit(); });
	UdpConnPtr con = UdpConn::createConnection(&base, SERVER_IP, SERVER_PORT);

	exitif(!con, "start udp conn failed");

	con->onMsg([](const UdpConnPtr& p, Buffer buf) {
//		info("udp recved %lu bytes", buf.size());
		xpackage package;
		bool suc = package.ParseFromArray(buf.data(), buf.size());
		if (!suc) {
			warn("decode failed");
			return;
		}
		if (package.cmd() == CMD_GET_DATA) {
			xpackage pa;
			for(int i = 0; i < package.lost_id_size() && pa.ByteSize() < 450;i++){
				int lost_id = package.lost_id(i);
				if(g_unack_msg.find(lost_id) != g_unack_msg.end()){
					info("retransmit:%d", lost_id);
					xmsg* m = pa.add_msg();
					*m = *(g_unack_msg[lost_id]);
				}
			}
		
			char b[510] = {};
			pa.SerializeToArray(b, sizeof(b));
			p->send(b, pa.ByteSize());
		}else if(package.cmd() == CMD_FINISH){
			g_exit = 1;
		}
	});

	base.runAfter(0, [&]() {
		if(g_id <= MAX_MSG_ID){
			shared_ptr<xmsg> msg = gen_msg();
			info("id:%d,time:%ld",msg->id(), util::timeMilli());
			g_unack_msg[msg->id()] = msg;

			char b[500] = {};
			int l = get_content(b, sizeof(b));
			if (l > 0) {
				con->send(b,l);
			}
		}else if (g_exit){
			base.exit();
		}
	}, MSG_INTERVAL);

	base.loop();
}
